package com.learn.demo.webserver;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.learn.demo.enums.WebSocketMessageTypeEnum;
import com.learn.demo.utils.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * webSocket 服务
 *
 * @author banjiawei
 * @date 2021/05/25
 */
@ServerEndpoint("/scan/webSocket/{roomId}")
@Component
@Slf4j
public class ClusterWebSocketServer {


    /**
     * 保存用户session的映射关系
     */
    private static ConcurrentHashMap<String, List<Session>> roomUserInfoMap = new ConcurrentHashMap<>();

    /**
     * 收到消息调用的方法，群成员发送消息
     *
     * @param session
     * @param message
     * @return void
     * @author banjiawei
     * @date 2021/5/11
     */
    @OnMessage
    public void onMessage(Session session, @PathParam("roomId") String roomId, String message) {
        if (StringUtils.isBlank(message)) {
            log.warn("接收消息为空, roomId : {}", roomId);
            return;
        }
        if ("ping".equals(message)) {
            //心跳信息
            sendClusterMessage(WebSocketMessageTypeEnum.ON_MESSAGE, "pong");
            return;
        }
        JSONObject jsonObject = JSON.parseObject(message);
        //获取事件名
        if (!jsonObject.containsKey("event")) {
            log.warn("参数不包含事件名event, roomId : {}", roomId);
            return;
        }
        String eventName = jsonObject.getString("event");
        //获取枚举类
        WebSocketMessageTypeEnum messageTypeEnum = WebSocketMessageTypeEnum.getWebSocketMessageTypeEnum(eventName);
        sendClusterMessage(messageTypeEnum, "服务器返回消息");

    }

    /**
     * 建立连接调用的方法，群成员加入
     *
     * @param session
     * @return void
     * @author banjiawei
     * @date 2021/5/11
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("roomId") String roomId) {
        List<Session> roomSessions = roomUserInfoMap.get(roomId);
        if (roomSessions == null) {
            roomSessions = new ArrayList<>();
            roomUserInfoMap.put(roomId, roomSessions);
        }
        //添加本地session
        roomSessions.add(session);
        //添加人数
        addClusterSessionCount(roomId, session);
        //获取房间号对应的人数
        long onlineNum = getClusterSessionCount(roomId);
        log.info("连接建立,房间号: {},当前集群在线人数:{} ", roomId, onlineNum);
    }

    /**
     * 关闭连接调用的方法，群成员退出
     *
     * @param session
     * @return void
     * @author banjiawei
     * @date 2021/5/11
     */
    @OnClose
    public void onClose(Session session, @PathParam("roomId") String roomId) {
        //获取本地同批次session
        List<Session> sessionList = roomUserInfoMap.get(roomId);
        //移除本地session
        sessionList.remove(session);
        //移除集群同批次人数
        removeClusterSessionCount(roomId, session);
        //获取集群在线人数
        long onlineNum = getClusterSessionCount(roomId);
        log.info("连接关闭, 房间号: {},当前集群在线人数:{} ", roomId, onlineNum);
    }

    /**
     * 传输消息错误调用的方法
     *
     * @param error
     */
    @OnError
    public void OnError(Throwable error) {
        log.info("连接出错：{}", error.getMessage());
    }

    /**
     * 发布集群的消息
     *
     * @param messageTypeEnum
     * @return void
     * @author banjiawei
     * @date 2021/5/11
     */
    private void sendClusterMessage(WebSocketMessageTypeEnum messageTypeEnum, String message) {
        RedisTemplate<String, Object> redisTemplate15 = SpringContextUtil.getBean("redisTemplate15", RedisTemplate.class);
        redisTemplate15.convertAndSend(messageTypeEnum.getTopName(), message);
    }

    /**
     * 添加集群在线人数
     *
     * @param roomId
     * @param session
     * @return void
     * @author banjiawei
     * @date 2021/5/11
     */
    private void addClusterSessionCount(String roomId, Session session) {
        RedisTemplate<String, Object> redisTemplate15 = SpringContextUtil.getBean("redisTemplate15", RedisTemplate.class);
        redisTemplate15.opsForZSet().add(roomId, session.getId(), System.currentTimeMillis());
    }

    /**
     * 移除集群在线的点餐人数
     *
     * @param roomId
     * @param session
     * @return void
     * @author banjiawei
     * @date 2021/5/11
     */
    private void removeClusterSessionCount(String roomId, Session session) {
        RedisTemplate<String, Object> redisTemplate15 = SpringContextUtil.getBean("redisTemplate15", RedisTemplate.class);
        redisTemplate15.opsForZSet().remove(roomId, session.getId());
    }

    /**
     * 获取集群在线的点餐人数
     *
     * @param roomId
     * @return long
     * @author banjiawei
     * @date 2021/5/11
     */
    private long getClusterSessionCount(String roomId) {
        RedisTemplate<String, Object> redisTemplate15 = SpringContextUtil.getBean("redisTemplate15", RedisTemplate.class);
        return redisTemplate15.opsForZSet().size(roomId) == null ? 0 : redisTemplate15.opsForZSet().size(roomId);
    }

    /**
     * 发布webSocket消息
     *
     * @param message
     * @return
     */
    public void sendClusterWebSocketMessage(String message) {
        if (StringUtils.isNotBlank(message)) {
            //得到当前群的所有会话，也就是所有用户,message需要带有roomId
            List<Session> userSessions = roomUserInfoMap.get(message);
            if (!CollectionUtils.isEmpty(userSessions)) {
                // 遍历Session集合给每个会话发送文本消息
                userSessions.forEach(item -> {
                    try {
                        item.getBasicRemote().sendText(message);
                    } catch (Exception e) {
                        log.warn("会话异常！" + e.getMessage());
                        e.printStackTrace();
                    }
                });
            }

        }
    }
}
